using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//using Poly2Tri;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using PolyDecomp;

namespace GameStateManagementSample.Classes
{
    using Polygons = List<List<ClipperLib.IntPoint>>;
    using ClipperLib;

    class Area
    {
        protected List<Region> regions = new List<Region>();

        public Color color { get; set; }


        public bool PointOnBorder(Vector2 pnt)
        {
            foreach (var reg in regions)
            {
                if (reg.PointOnBorder(pnt))
                {
                    return true;
                }
            }
            return false;
        }
       
        public void AddPolygon(List<Vector2> poly)
        {
            Polygons solution = new Polygons();

            Clipper c = new Clipper();
            foreach (var reg in regions) 
            {
                var polygon = reg.Vectors.Select(f=>(IntPoint)f).ToList();
                c.AddPolygon(polygon, PolyType.ptSubject);
            }

            c.AddPolygon(poly.Select(f => (IntPoint)f).ToList(), PolyType.ptSubject);

            bool ret = c.Execute(ClipType.ctUnion, solution,
               PolyFillType.pftEvenOdd, PolyFillType.pftEvenOdd);

            if (ret)
            {
                solution = Clipper.OffsetPolygons(solution,1, JoinType.jtRound);
                solution = Clipper.OffsetPolygons(solution, -1, JoinType.jtRound);
                List<Region> regs = new List<Region>();
                List<List<Vector2>> holes = new List<List<Vector2>>();
                foreach (var pol in solution) 
                {
                    List<Vector2> polygon = pol.Select(f => (Vector2)f).ToList();
                   if (GeometryHelper.IsClockwisePolygon(polygon))
                   {
                       regs.Add(new Region(polygon, color));
                   }
                   else
                   {
                       holes.Add(polygon);
                   }

                }
                regions = regs;

                foreach (var hole in holes) 
                {
                    foreach (var reg in regions)
                    {
                        if (reg.HitTest(hole[0])) 
                        {
                            reg.CreateHole(hole);
                        }
                    }
                }

                //union = retUnion.ToList();
               
            }
            



            //List<Region> toremove = new List<Region>();
            //this.regions.Add(new Region(poly, color));
            //bool[] covered = new bool[regions.Count];
            //for (int i = 0; i < regions.Count; i++)
            //{
            //    covered[i] = false;
            //}

           
            //for(int i =0; i<regions.Count;i++)
            //{
            //    if (covered[i]) continue;
            //    for(int k =0; k<regions.Count;k++)
            //    {
            //        if (regions[i] == regions[k]) continue;
            //        if (regions[i].AddPolygon(regions[k].Vectors)) 
            //        {
            //            toremove.Add(regions[k]);
            //            covered[k] = true;
            //        }
            //    }

            //}
            //foreach (var reg in toremove)
            //{
            //    regions.Remove(reg);
            //}
            
        }

        public bool PointOnBorderAndRetRegion(Vector2 pnt, out Region region)
        {
            region = null;
            foreach (var reg in regions)
            {
                if (reg.PointOnBorder(pnt))
                {
                    region = reg;
                    return true;
                }
            }
            return false;
        }
        public void SetArea(List<Vector2> points)
        {
            regions.Clear();
            regions.Add(new Region(points,color));
        }

        public void SplitArea(SmoothPath sp, out List<Vector2> poly1, out List<Vector2> poly2)
        {
            poly1 = null; poly2 = null;
            Region r1, r2;
            PointOnBorderAndRetRegion(sp.pathPoints.First(), out r1);
            PointOnBorderAndRetRegion(sp.pathPoints.Last(), out r2);
            if (r1 != null && r2 != null && r1 == r2)
            {
                r1.SplitArea(sp, out  poly1, out poly2);
            }
            else
            {
                throw new Exception("wtf");
                return;
            }
        }


        public Vector2 GetTrajectoryIntersection(Vector2 start, Vector2 end)
        {
            if (regions.Count == 0) return -Vector2.One;

            Vector2 retPnt = -Vector2.One;

            var distance = 20000f;
            foreach (var reg in regions)
            {
                var pnt = reg.GetTrajectoryIntersection(start, end);
                if (pnt != null)
                {
                    var tmpdist = Vector2.Distance(end, pnt.Value);
                    if (tmpdist < distance) { distance = tmpdist; retPnt = pnt.Value; }
                }

            }
            return retPnt;
        }

        public Vector2 GetClosestPointOnAreaBorder(Vector2 P)
        {
            if (regions.Count == 0) return -Vector2.One;
            // var poly = Vectors;
            Vector2 retPnt = -Vector2.One;
            //Vector2 last = poly.Last();
            var distance = 2000f;// Vector2.Distance(P, poly.First());
            foreach (var reg in regions)
            {
                var pnt = reg.GetClosestPointOnAreaBorder(P);
                var tmpdist = Vector2.Distance(P, pnt);
                if (tmpdist < distance) { distance = tmpdist; retPnt = pnt; }
                // last = vert;
            }
            return retPnt;
        }
        public Vector2 GetClosestPointOnAreaBorderAndRegion(Vector2 P, out Region region)
        {
            region = null;
            if (regions.Count == 0) return -Vector2.One;
            // var poly = Vectors;
            Vector2 retPnt = -Vector2.One;
            //Vector2 last = poly.Last();
            var distance = 2000f;// Vector2.Distance(P, poly.First());
            foreach (var reg in regions)
            {
                var pnt = reg.GetClosestPointOnAreaBorder(P);
                var tmpdist = Vector2.Distance(P, pnt);
                if (tmpdist < distance)
                {
                    distance = tmpdist; retPnt = pnt;
                    region = reg;
                }

                // last = vert;
            }
            return retPnt;
        }
        public virtual void Draw(SpriteBatch spriteBatch)
        {
            try
            {

                foreach (var reg in regions)
                {
                    //reg.FillColor = color;
                    reg.Draw(spriteBatch);
                }
            }
            catch { }

        }
        public bool HitTest(Vector2 pnt)
        {
            if (regions.Count == 0) return false;
            // Vector2 last = polygon.Last();
            // bool c = false;
            foreach (var reg in regions)
            {
                if (reg.HitTest(pnt)) return true;
                //  last = vert;
            }


            return false;

        }
        public bool HitTestAndRegion(Vector2 pnt, out Region region)
        {
            region = null;
            if (regions.Count == 0) return false;
            // Vector2 last = polygon.Last();
            // bool c = false;
            foreach (var reg in regions)
            {
                if (reg.HitTest(pnt))
                {
                    region = reg;
                    return true;
                }
                //  last = vert;
            }


            return false;

        }
        public Vector2 getPointNormalOffset(Vector2 point, float offset)
        {
            Region region = null;
            if (!PointOnBorder(point))
            {
                point = GetClosestPointOnAreaBorderAndRegion(point, out region);
            }


            var poly = region.Vectors;

            Vector2 last = poly.Last();

            foreach (var vert in poly)
            {
                if (GeometryHelper.PointIsOnLine(last, vert, point))
                {

                    Vector2 pnt = (vert - last);
                    var rot = (float)Math.Atan2(pnt.X, -pnt.Y) - MathHelper.PiOver2;


                    return new Vector2(point.X + offset * (float)Math.Sin(rot), point.Y - offset * (float)Math.Cos(rot));

                    //Vector2 vect = vert-last*;
                    //var rot = (float)Math.Atan2(vect.Y, vect.X);


                    //new Vector2(vect.X + 40 * (float)Math.Sin(rot), vect.Y - 40 * (float)Math.Cos(rot))

                    //tween_position = Vector2.Transform(tween_position, m);
                }
                last = vert;
            }
            return -Vector2.One;
        }
    }
}
